package model;

import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;

import javax.jdo.Extent;
import javax.jdo.PersistenceManager;
import javax.jdo.identity.ObjectIdentity;

import app.PMF;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;

public class Util {
	public static final int NAJBRZI = 1;
	public static final int NAJJEFTINIJI = 2;

	/**
	 * Vraca iz baze objekat Mesto na osnovu zadatog imena
	 * */
	public static Mesto getMestoFromName(String name) {
		PersistenceManager pm = PMF.get().getPersistenceManager();

		Extent<Mesto> ex = pm.getExtent(Mesto.class, true);
		Iterator<Mesto> i = ex.iterator();
		while (i.hasNext()) {
			Mesto mesto = i.next();
			if (mesto.getNaziv().equals(name)) {
				pm.close();
				return mesto;
			}
		}

		pm.close();
		return null;
	}
	
	/**
	 * Vraca iz baze objekat Prevoznik na osnovu zadatog naziva
	 * Trazi i po nazivu i po alias-u
	 * */
	public static Prevoznik getCarrierFromName(String name) {
		PersistenceManager pm = PMF.get().getPersistenceManager();

		Extent<Prevoznik> ex = pm.getExtent(Prevoznik.class, true);
		Iterator<Prevoznik> i = ex.iterator();
		while (i.hasNext()) {
			Prevoznik prevoznik = i.next();
			if (prevoznik.getNaziv().equals(name)) {
				pm.close();
				return prevoznik;
			}
			//ako nije nadjen, trazimo ga po aliasu
			for(String alias : prevoznik.getAliases()){
				if (alias.equals(name)) {
					pm.close();
					return prevoznik;
				}
			}
		}

		pm.close();
		return null;
	}
	
	/**
	 * Ova metoda treba da izdvoji koriniku putanje od polazista do odredista po
	 * kriterijumu(Tip) UPDATE: vraca samo sve moguce putanje izmedju dva mesta
	 * */
	public static Vector<PutnLinija> getPaths(Mesto fromTown, Mesto toTown,
			Vector<PutnaDuz> paths, int Tip) {

		// putna duz do cilja
		Vector<PutnaDuz> rez = new Vector<PutnaDuz>();

		// sve putne duzi do cilja
		Vector<Vector<PutnaDuz>> allRez = new Vector<Vector<PutnaDuz>>();

		// mesta koja sam pretrazio
		Vector<Mesto> bioSam = new Vector<Mesto>();

		piciDalje(fromTown, toTown, bioSam, rez, paths, allRez);

		System.out.println("######### br resenja ##########");
		System.out.println(allRez.size());
		System.out.println("############################");

		// TODO
		// e sad ovde treba da sredim putanje po kojem hocu kriterijumu

		Vector<PutnLinija> retVal = new Vector<PutnLinija>();

		System.out.println("RezSize: " + allRez.size());
		System.out.println(allRez.get(0));

		for (Vector<PutnaDuz> el : allRez) {
			System.out.println("ElSize: " + el.size());
			retVal.add(new PutnLinija(el));
		}

		return retVal;
	}

	/**
	 * Metoda sluzi za backtracking koji je rekurzivno poziva da bi se pronasle
	 * sve putanje
	 * 
	 * @param bioSam
	 *            gradove u koje ne smije dalje da ide
	 * @param rez
	 *            pronadjene putanje do sada
	 * @return rez pronadjene putanje s novim
	 */
	@SuppressWarnings("unchecked")
	public static Vector<PutnaDuz> piciDalje(Mesto home, Mesto cilj,
			Vector<Mesto> bioSam, Vector<PutnaDuz> rez, Vector<PutnaDuz> paths,
			Vector<Vector<PutnaDuz>> allRez) {
		// nasao si putanju do cilja?
		// if(home.equals(cilj))
		if (home.getNaziv().equals(cilj.getNaziv())) {
			System.out.println("### da li je " + home.getNaziv() + " = "
					+ cilj.getNaziv() + " DA !");

			// ubaci putanju u listu resenja
			System.out.println("dodato??"
					+ allRez.add((Vector<PutnaDuz>) rez.clone()));
			System.out.println("<><><><><><> dodato jedno resenje:");
			for (int i = 0; i < rez.size(); i++)
				System.out.print("ID:" + rez.get(i).getSifra().toString()
						+ "od " + rez.get(i).getMestoOd().getNaziv() + " do "
						+ rez.get(i).getMestoDo().getNaziv() + " <> ");
			System.out.println();
			System.out.println("<><><><><><>");

		}
		// ako nisi, trazi dalje
		else {
			System.out.println("### da li je " + home.getNaziv() + " = "
					+ cilj.getNaziv() + " NE !");

			System.out.println("*** home:" + home.getNaziv() + " "
					+ home.getSifra() + " cilj:" + cilj.getNaziv() + " "
					+ cilj.getSifra());

			// stavim trenutno mjesto u listu predjenih mjesta
			bioSam.add(home);

			// lista mjesta gde sve mogu otici
			Vector<PutnaDuz> pom = getGdeSveMoguOtici(home, paths);

			// prodji kroz svaki
			for (int i = 0; i < pom.size(); i++) {
				// ako sam bio u tom mjestu ne idi vise, preskoci ga, ako nisam
				// idi u njega
				if (!bioSam.contains(pom.get(i))) {
					System.out.println("### idi u "
							+ pom.get(i).getMestoDo().getNaziv());

					rez.add(pom.get(i));
					System.out.println("### dodat u rez "
							+ pom.get(i).getSifra());

					piciDalje(pom.get(i).getMestoDo(), cilj, bioSam, rez,
							paths, allRez);

					System.out.println("### obrisan iz rez "
							+ pom.get(i).getSifra());
					rez.remove(pom.get(i));

					System.out.println("<<<<>>>>>" + allRez.get(0));
				}

			}
		}
		return rez;
	}

	/**
	 * Ovoj metodi se salje mesto, i ona vraca sve putne duzi kojim se iz tog
	 * mesto moze otici u neko drugo mesto.
	 **/

	public static Vector<PutnaDuz> getGdeSveMoguOtici(Mesto home,
			Vector<PutnaDuz> paths) {
		Vector<PutnaDuz> rez = new Vector<PutnaDuz>();

		for (int i = 0; i < paths.size(); i++)
			if (paths.get(i).getMestoOd().getNaziv().equals(home.getNaziv()))
				rez.add(paths.get(i));

		System.out.println("Gde sve mogu otici iz " + home.getNaziv());
		for (int i = 0; i < rez.size(); i++)
			System.out.println(rez.get(i).getMestoDo());

		return rez;
	}

	/**
	 * Super,Turbo...sumo metoda!
	 * */
	public static <T> Vector<T> getAllEntitiesFromTable(Class<T> T) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Vector<T> vec = new Vector<T>();

		Extent<T> ex = pm.getExtent(T, true);
		Iterator<T> i = ex.iterator();
		while (i.hasNext()) {
			T obj = i.next();
			vec.add(obj);
		}
		pm.close();
		return vec;
	}

	public static <T> T getEntityFromTableByID(Class<T> T, String ID) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		T obj = null;

		Extent<T> ex = pm.getExtent(T, true);
		Iterator<T> i = ex.iterator();
		while (i.hasNext()) {
			obj = i.next();
			// Key key = (Key) pm.getObjectId(obj);
			ObjectIdentity oi = (ObjectIdentity) pm.getObjectId(obj);
			Key key = (Key) oi.getKey();
			if (ID.equals(key.getId() + "")) {
				pm.close();
				return obj;
			}
		}
		pm.close();
		return obj;
	}
	
	public static <T> T getEnityByFieldEqualsValue(Class<T> T, String field, String value) {
		PersistenceManager pm = PMF.get().getPersistenceManager();

		DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
		Query q = new Query(T.getSimpleName());

		q.addFilter(field, FilterOperator.EQUAL, value);
		PreparedQuery pq = ds.prepare(q);

		for (Entity result : pq.asIterable()) {
			Key key = (Key) result.getKey();
			T retVal = (T)pm.getObjectById(T, key);
			pm.close();
			return retVal;
		}
		pm.close();
		return null;
	}

	public static User getUser(String username, String password) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		User obj = null;

		Extent<User> ex = pm.getExtent(User.class, true);
		Iterator<User> i = ex.iterator();

		while (i.hasNext()) {
			obj = i.next();
			if (obj.getUsername().equals(username)
					&& obj.getPassword().equals(password)) {
				pm.close();
				return obj;
			}
		}
		pm.close();
		return obj;
	}

	public static Prevoznik getPrevoznikByUser(User user) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		Prevoznik obj = null;

		Extent<Prevoznik> ex = pm.getExtent(Prevoznik.class, true);
		Iterator<Prevoznik> i = ex.iterator();
		while (i.hasNext()) {
			obj = i.next();
			if (obj.getKorisnik().equals(user)) {
				pm.close();
				return obj;
			}
		}
		pm.close();
		return obj;
	}

	/**
	 * Vadimo iz baze filtrirane rezultate po prevozniku u rastucem redosledu po
	 * polasku
	 * */
	public static Vector<PutnaDuz> getAllTripsByCarrier(Prevoznik prevoznik) {
		PersistenceManager pm = PMF.get().getPersistenceManager();

		Vector<PutnaDuz> trips = new Vector<PutnaDuz>();
		DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
		Query q = new Query(PutnaDuz.class.getSimpleName());
		System.out.println(PutnaDuz.class.getSimpleName());
		q.addFilter("prevoznik", FilterOperator.EQUAL, prevoznik.getSifra());
		q.addSort("polazak", SortDirection.ASCENDING);
		PreparedQuery pq = ds.prepare(q);

		for (Entity result : pq.asIterable()) {
			Key prev = (Key) result.getProperty("prevoznik");
			System.out.println("PrevoznikID: " + prev.getId());
			if (prev.equals(prevoznik.getSifra())) {
				PutnaDuz pd = pm.getObjectById(PutnaDuz.class, result.getKey());
				trips.add(pd);
			}
		}
		pm.close();
		return trips;

	}

	public static Vector<Povlastica> getBensForCarrier(Prevoznik prevoznik) {
		PersistenceManager pm = PMF.get().getPersistenceManager();

		Vector<Povlastica> bens = new Vector<Povlastica>();
		DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
		Query q = new Query(Povlastica.class.getSimpleName());
		System.out.println(Povlastica.class.getSimpleName());
		q.addFilter("prevoznik", FilterOperator.EQUAL, prevoznik.getSifra());
		// q.addSort("polazak", SortDirection.ASCENDING);
		PreparedQuery pq = ds.prepare(q);

		for (Entity result : pq.asIterable()) {
			Key prev = (Key) result.getProperty("prevoznik");
			System.out.println("PrevoznikID: " + prev.getId());
			if (prev.equals(prevoznik.getSifra())) {
				Povlastica pd = pm.getObjectById(Povlastica.class,
						result.getKey());
				bens.add(pd);
			}
		}
		pm.close();
		return bens;
	}

	public static Date subTwoDates(Date date1, Date date2) {
		Calendar cal1 = Calendar.getInstance();
		Calendar cal2 = Calendar.getInstance();
		cal1.setTime(date1);
		cal2.setTime(date2);
		// oduzimamo minute i sate
		cal1.add(Calendar.MINUTE, -1 * cal2.get(Calendar.MINUTE));
		cal1.add(Calendar.HOUR, -1 * cal2.get(Calendar.HOUR));

		return cal1.getTime();
	}

}